package net.zoneland.knowledge.controller;

import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import net.zoneland.knowledge.constant.Constants;
import net.zoneland.knowledge.controller.bean.ExcelSheetVo;
import net.zoneland.knowledge.controller.bean.SanHuiYiKeVo;
import net.zoneland.knowledge.controller.bean.UomUserOrgQueryVo;
import net.zoneland.knowledge.controller.util.PartyOrgTreeUtils;
import net.zoneland.knowledge.enums.*;
import net.zoneland.knowledge.exceptions.DjExceptionCode;
import net.zoneland.knowledge.model.*;
import net.zoneland.knowledge.service.PartyGroupService;
import net.zoneland.knowledge.service.PartyService;
import net.zoneland.knowledge.service.PurviewService;
import net.zoneland.knowledge.service.SanHuiYiKeService;
import net.zoneland.knowledge.utils.SanHuiYiKeReportColumnHandler;
import org.apache.commons.collections4.ListUtils;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 三会一课的REST.
 * @author zouxia
 */
@RestController()
@RequestMapping("/sanKe")
@Slf4j
public class SanHuiYiKeController extends  AbstractBaseController {

    @Autowired
    private SanHuiYiKeService sanHuiYiKeService;

    /**
     * 党员/党组织业务接口.
     */
    @Autowired
    private PartyService partyService;

    /**
     * 党组织小组业务接口.
     */
    @Autowired
    private PartyGroupService partyGroupService;

    @Autowired
    private PurviewService purviewService;

    private static final String TYPERECORD_DOUBLE_VERTICAL = "||";

    private static final String TYPERECORD_CAESURA_SIGN = "、";

    private static final String FILENAME = "三会一课巡检";

    private static final String EXPORTS_FILENAME = "三会一课数据导出";

    /**
     * 党组织等级2
     */
    private static final Integer ORGTREE_LEVEL_2 = 2;

    /**
     * 是
     */
    private static final String TRUE_STRING = "是";

    /**
     * 否
     */
    private static final String FALSE_STRING = "否";

    /**
     * 三会一课查询权限
     */
    private static final List<PurviewEnum> SHYK_LIST = ImmutableList.of(PurviewEnum.BRANCH_DW,
            PurviewEnum.PROVINCE_DW, PurviewEnum.PROVINCE_LEADER, PurviewEnum.BRANCH_LEADER, PurviewEnum.GANSHI);


    /**
     * 查询.
     * @param request http请求
     * @param vo 查询参数对象
     * @return 用户集合响应结果
     */
    @PostMapping("/query")
    public ResponseResultRest query(final HttpServletRequest request, @RequestBody final SanHuiYiKeVo vo) {
        Preconditions.checkNotNull(vo, DjExceptionCode.DJ_OBJ_NULL);
        final List<SanHuiYiKe> sanHuiYiKes = list(request, vo, null);
        conversionSanList(sanHuiYiKes);
        return ResponseResultRest.success(sanHuiYiKes);
    }

    /**
     * 查询.
     * @param request http请求
     * @param id 查询参数对象
     * @return 用户集合响应结果
     */
    @GetMapping("/getInfoById")
    public ResponseResultRest getInfoById(final String id, HttpServletRequest request) {
        Preconditions.checkNotNull(id, DjExceptionCode.DJ_ID_NULL);
        return ResponseResultRest.success(Arrays.asList(sanHuiYiKeService.getInfoById(id)));
    }

    /**
     * 查询公司列表.
     * @return 用户集合响应结果
     */
    @GetMapping("/getCompanyList")
    public ResponseResultRest getInfoById(final HttpServletRequest request) {
        return ResponseResultRest.success(PartyOrgTreeUtils.getPartyOrgVoForSelectList());
    }

    /**
     * 查询党组织相关信息.
     * @param request 请求.
     * @param vo 查询参数.
     * @return 人员信息
     */
    @PostMapping("/queryDepts")
    public ResponseResultRest queryDepts(final HttpServletRequest request, final @RequestBody UomUserOrgQueryVo vo) {
        final ResponseResultRest res = ResponseResultRest.success();
        final LoginUserPojo user = getAndCheckCurrentUser(request);
        final String companyName = user.getCompanyName();

        if (CmpDnEnum.ZMCC.getName().equals(companyName)) {
            return ResponseResultRest.success(PartyOrgTreeUtils.getPartyOrgVoForSelectList());
        }

        final List<PartyOrgVoForSelect> selectList = queryOrgTree(ORGTREE_LEVEL_2, CompanyEnum.getEnumByCompanyName(companyName).getOrgUuid());
        res.setData(selectList);
        return res;
    }

    //需求标红的列.
    private static  List<Integer> markRedList = Lists.newArrayList(SanHuiYiKeColumnEnum.COLUMN_INDEX_TWO.getIntKey(), SanHuiYiKeColumnEnum.COLUMN_INDEX_ONE.getIntKey());

    /**
     * pc导出.
     * @param request
     * @param response
     * @param vo 三会一课巡检参数
     */
    @ResponseBody
    @PostMapping("/download")
    public void batchDownPc(final HttpServletRequest request, final HttpServletResponse response, @RequestBody final SanHuiYiKeVo vo) throws IOException {
        batchDown(request, response, vo, null);
    }

    /**
     * 导出.
     * @param request request
     * @param response response
     * @param vo 三会一课巡检参数
     * @param token moa端token
     */
    private void batchDown(final HttpServletRequest request, final HttpServletResponse response, @RequestBody SanHuiYiKeVo vo, final String token) throws IOException{
        setExcelRespProp(response, FILENAME);

        // 合并组织uuids和组织小组uuids
        vo.setOrgUuids(unionPartyOrgUuidsAndPartyGroupOrgUuids(vo.getOrgUuids()));

        final List<SanHuiYiKe> sanHuiYiKes = list(request, vo, token);
        // 三会一课反向巡检信息列表
        final List<SanKeReverse> sanKeReverses = new ArrayList<>();
        // 三会一课巡检信息列表
        final List<SanHuiYiKe> sanHuiYiKeList = new ArrayList<>();
        // 转化三会一课列表字段
        conversionSanListSanKe(sanHuiYiKes, vo, sanKeReverses, sanHuiYiKeList);

        // 处理excel中sheet的数据
        final AtomicInteger index = new AtomicInteger(0);
        final List<ExcelSheetVo> excelSheets = Stream.of(sanHuiYiKeList, sanKeReverses).map(sheetList -> {
            final SanHuiYiKeExcelSheetEnum sanHuiYiKeExcelSheetEnum = SanHuiYiKeExcelSheetEnum.getBySheetIndex(index.get());
            final ExcelSheetVo excelSheetVo = new ExcelSheetVo();
            if (sanHuiYiKeExcelSheetEnum != null) {
                excelSheetVo.setSheetList(sheetList);
                excelSheetVo.setSheetClass(sanHuiYiKeExcelSheetEnum.getSheetClass());
                excelSheetVo.setSheetIndex(sanHuiYiKeExcelSheetEnum.getSheetIndex());
                excelSheetVo.setSheetName(sanHuiYiKeExcelSheetEnum.getSheetName());
            }
            index.getAndIncrement();
            return excelSheetVo;
        }).filter(excelSheetVo -> StringUtils.isNotBlank(excelSheetVo.getSheetName())).collect(Collectors.toList());

        this.sanHuiYiKeWrite(response, new SanHuiYiKeReportColumnHandler(), excelSheets, vo, markRedList);
    }

    /**
     * 三会一课数据导出.
     * @param request
     * @param response
     * @param vo 三会一课数据参数
     */
    @ResponseBody
    @PostMapping("/exports")
    public void exports(final HttpServletRequest request, final HttpServletResponse response, @RequestBody final SanHuiYiKeVo vo) throws IOException {
        batchExports(request, response, vo, null);
    }

    /**
     * 三会一课数据导出.
     * @param request request
     * @param response response
     * @param vo 三会一课巡检参数
     * @param token moa端token
     */
    private void batchExports(final HttpServletRequest request, final HttpServletResponse response, @RequestBody SanHuiYiKeVo vo, final String token) throws IOException{
        setExcelRespProp(response, EXPORTS_FILENAME);

        // 合并组织uuids和组织小组uuids
        vo.setOrgUuids(unionPartyOrgUuidsAndPartyGroupOrgUuids(vo.getOrgUuids()));

        // 获取三会一课数据
        final List<SanHuiYiKe> sanHuiYiKes = getThreeMeetingsList(request, vo, token);

        // 三会一课巡检信息列表,转化三会一课列表字段
        final List<SanHuiYiKe> sanHuiYiKeList = convertThreeMeetingsFields(sanHuiYiKes);

        // 处理excel中sheet的数据
        final AtomicInteger index = new AtomicInteger(0);
        final List<ExcelSheetVo> excelSheets = Stream.of(sanHuiYiKeList).map(sheetList -> {
            final SanHuiYiKeExcelSheetEnum sanHuiYiKeExcelSheetEnum = SanHuiYiKeExcelSheetEnum.getBySheetIndex(index.get());
            final ExcelSheetVo excelSheetVo = new ExcelSheetVo();
            if (sanHuiYiKeExcelSheetEnum != null) {
                excelSheetVo.setSheetList(sheetList);
                excelSheetVo.setSheetClass(sanHuiYiKeExcelSheetEnum.getSheetClass());
                excelSheetVo.setSheetIndex(sanHuiYiKeExcelSheetEnum.getSheetIndex());
                excelSheetVo.setSheetName(sanHuiYiKeExcelSheetEnum.getSheetName());
            }
            index.getAndIncrement();
            return excelSheetVo;
        }).filter(excelSheetVo -> StringUtils.isNotBlank(excelSheetVo.getSheetName())).collect(Collectors.toList());

        // 数据导出没有关键字条件，写excel的方法有对关键字的样式设置，此处设置关键字为空。
        vo.setMeetingName("");
        this.sanHuiYiKeWrite(response, new SanHuiYiKeReportColumnHandler(), excelSheets, vo, markRedList);
    }

    /**
     * 获取三会一课数据list列表处理:当前用户是省公司的人，则可以查询所有记录；否则只有干事才能查看所属公司记录.
     * @param request
     * @param vo 查询的对象
     * @param token token
     * @return 过滤后的列表信息.
     */
    private List<SanHuiYiKe> getThreeMeetingsList(final HttpServletRequest request, final SanHuiYiKeVo vo, final String token) {
        final LoginUserPojo user = getAndCheckCurrentUser(request, token);
        //党建后台的批量巡检功能 调用该接口,但未经过securityfilter导致user中没有权限信息,所以重新获取权限放到用户信息中
        if(Objects.isNull(user.getPurviewEnums())) {
            user.setPurviewEnums(purviewService.getPurviewEnumsByUser(user));
        }
        if (!CollectionUtils.containsAny(SHYK_LIST, user.getPurviewEnums())) {
            return Collections.emptyList();
        }
        vo.setUser(user);
        final List<SanHuiYiKe> sanHuiYiKes = sanHuiYiKeService.searchNoMeetingsNameList(vo);
        final String companyName = user.getCompanyName();
        if (CmpDnEnum.ZMCC.getName().equals(companyName)) {
            return sanHuiYiKes;
        } else {
            return sanHuiYiKes.stream()
                .filter(san -> StringUtils.equals(san.getCompanyName(), companyName))
                .collect(Collectors.toList());
        }
    }

    /**
     * 转化三会一课列表字段.
     * @param sanHuiYiKes 三会一课列表巡检信息
     * @return sanHuiYiKeList 三会一课列表巡检全部信息
     */
    private List<SanHuiYiKe> convertThreeMeetingsFields(List<SanHuiYiKe> sanHuiYiKes) {

        return sanHuiYiKes.stream().sorted((o1, o2) -> CompanyEnum.comparator(o1.getCompanyName(), o2.getCompanyName()))
                .peek(bean -> {
                    //党组织uuid:除了党组织，也有可能是党小组的uuid，对应的是AITK_PARTY_GROUP
                    final String uuid = bean.getOrgUuid().toUpperCase(Locale.ROOT);
                    final PartyOrg partyOrg = partyService.getOrgByUuid(uuid);

                    bean.setOrgUuid(Optional.ofNullable(partyOrg).map(org -> {
                            return org.getOrgShortName();
                        }).orElseGet(() -> {
                            final PartyGroup group = partyService.getGroupByUuid(uuid);
                            return Optional.ofNullable(group)
                                .map(partyGroup -> group.getOrgGroupParentName()
                                    + Constants.STRING_SPLIT_FLAG_MIDDLE_LINE
                                    + group.getOrgGroupName())
                                .orElse("");
                        })
                    );

                    //会议状态
                    bean.setMeetingStatus(MeetingStatusEnum.getByIntKey(bean.getMeetingStatus()));
                    //会议类型
                    final String typeRecord = Optional.ofNullable(bean.getTypeRecord())
                        .map(type -> StringUtils.split(type, TYPERECORD_DOUBLE_VERTICAL))
                        .map(Arrays::stream)
                        .orElseGet(Stream::empty)
                        .filter(StringUtils::isNotBlank)
                        .filter(Predicate.isEqual(TYPERECORD_DOUBLE_VERTICAL).negate())
                        .map(RecordTypeEnum::getByIntKey)
                        .collect(Collectors.joining(TYPERECORD_CAESURA_SIGN));

                    bean.setTypeRecord(typeRecord);
                }).collect(Collectors.toList());

    }

    /**
     * 转化列表字段.
     * @param sanHuiYiKes
     */
    private void conversionSanList(final List<SanHuiYiKe> sanHuiYiKes) {
        sanHuiYiKes.sort((o1, o2) -> CompanyEnum.comparator(o1.getCompanyName(), o2.getCompanyName()));

        sanHuiYiKes.forEach(bean -> {
                    //党组织uuid:除了党组织，也有可能是党小组的uuid，对应的是AITK_PARTY_GROUP
                    final String uuid = bean.getOrgUuid().toUpperCase(Locale.ROOT);
                    final PartyOrg partyOrg = partyService.getOrgByUuid(uuid);
                    bean.setOrgUuid(Optional.ofNullable(partyOrg).map(PartyOrg::getOrgShortName)
                            .orElseGet(() -> {
                                final PartyGroup group = partyService.getGroupByUuid(uuid);
                                return Optional.ofNullable(group)
                                        .map(partyGroup -> group.getOrgGroupParentName()
                                                + Constants.STRING_SPLIT_FLAG_MIDDLE_LINE
                                                + group.getOrgGroupName())
                                        .orElse("");
                            })
                        );

                    //会议状态
                    bean.setMeetingStatus(MeetingStatusEnum.getByIntKey(bean.getMeetingStatus()));
                    //会议类型
                    final String typeRecord = Optional.ofNullable(bean.getTypeRecord())
                            .map(type -> StringUtils.split(type, TYPERECORD_DOUBLE_VERTICAL))
                            .map(Arrays::stream)
                            .orElseGet(Stream::empty)
                            .filter(StringUtils::isNotBlank)
                            .filter(Predicate.isEqual(TYPERECORD_DOUBLE_VERTICAL).negate())
                            .map(RecordTypeEnum::getByIntKey)
                            .collect(Collectors.joining(TYPERECORD_CAESURA_SIGN));

                    bean.setTypeRecord(typeRecord);
                })
        ;

    }

    /**
     * 反向查询党组织.
     * @param sanHuiYiKes 查询对象.
     * @return 返回不在列表中的党组织信息集合.
     */
    private List<SanHuiYiKeReverse> reverseQueryOrg(final List<SanHuiYiKe> sanHuiYiKes) {
        return sanHuiYiKes
                .stream()
                .map(SanHuiYiKe::getCompanyName)
                .distinct()
                .map(CompanyEnum::getEnumByCompanyName)
                .map(CompanyEnum::getOrgUuid)
                .flatMap(orgUuid ->
                        queryOrgTree(ORGTREE_LEVEL_2, orgUuid).stream()
                            .filter(Predicate.isEqual(orgUuid).negate())
                )
                .map(PartyOrgVoForSelect::getOrgShortName)
                .map(SanHuiYiKeReverse::new)
                .collect(Collectors.toList());
    }

    /**
     * list列表处理:当前用户是省公司的人，则可以查询所有记录；否则只有干事才能查看所属公司记录.
     * @param request
     * @param vo 查询的对象
     * @param token token
     * @return 过滤后的列表信息.
     */
    private List<SanHuiYiKe> list(final HttpServletRequest request, final SanHuiYiKeVo vo, final String token) {
        final LoginUserPojo user = getAndCheckCurrentUser(request, token);
        //党建后台的批量巡检功能 调用该接口,但未经过securityfilter导致user中没有权限信息,所以重新获取权限放到用户信息中
        if(Objects.isNull(user.getPurviewEnums())) {
            user.setPurviewEnums(purviewService.getPurviewEnumsByUser(user));
        }
        if (!CollectionUtils.containsAny(SHYK_LIST, user.getPurviewEnums())) {
            return Collections.emptyList();
        }
        vo.setUser(user);
        final List<SanHuiYiKe> sanHuiYiKes = sanHuiYiKeService.searchList(vo);
        final String companyName = user.getCompanyName();
        if (CmpDnEnum.ZMCC.getName().equals(companyName)) {
            return sanHuiYiKes;
        } else {
            return sanHuiYiKes.stream()
                    .filter(san -> StringUtils.equals(san.getCompanyName(), companyName))
                    .collect(Collectors.toList());
        }
    }

    /**
     * 导出get.
     * @param request
     * @param response
     * @param sanHuiYiKeVo 三会一课巡检参数
     * @param token moa端带token
     */
    @ResponseBody
    @RequestMapping(value = "/downloadGet", method = RequestMethod.GET)
    public void downloadGet(final HttpServletRequest request, final HttpServletResponse response, final SanHuiYiKeVo sanHuiYiKeVo, final String token) throws IOException {
        try {
            sanHuiYiKeVo.setMeetingName(URLDecoder.decode(sanHuiYiKeVo.getMeetingName(), StandardCharsets.UTF_8.name()));
            sanHuiYiKeVo.setCompanyName(URLDecoder.decode(sanHuiYiKeVo.getCompanyName(), StandardCharsets.UTF_8.name()));
            batchDown(request, response, sanHuiYiKeVo, token);
        }catch (UnsupportedEncodingException e) {
            log.error("URLDecoder error {}:", sanHuiYiKeVo, e);
        }
    }

    /**
     * 转化三会一课列表字段.
     * @param sanHuiYiKes 三会一课列表巡检信息
     * @param vo SanHuiYiKeVo
     * @param sanKeReverses 三会一课列表反向巡检信息
     * @param sanHuiYiKeList 三会一课列表巡检全部信息
     */
    private void conversionSanListSanKe(List<SanHuiYiKe> sanHuiYiKes, final SanHuiYiKeVo vo, final List<SanKeReverse> sanKeReverses, final List<SanHuiYiKe> sanHuiYiKeList) {

        // 党总支和党支部的uuid
        final Set<String> orgUuids = new HashSet<>();
        sanHuiYiKeList.addAll(
                sanHuiYiKes.stream().sorted((o1, o2) -> CompanyEnum.comparator(o1.getCompanyName(), o2.getCompanyName()))
                .peek(bean -> {
                    //党组织uuid:除了党组织，也有可能是党小组的uuid，对应的是AITK_PARTY_GROUP
                    final String uuid = bean.getOrgUuid().toUpperCase(Locale.ROOT);
                    final PartyOrg partyOrg = partyService.getOrgByUuid(uuid);

                    bean.setOrgUuid(Optional.ofNullable(partyOrg).map(org -> {
                                orgUuids.add(uuid);
                                return org.getOrgShortName();
                            }).orElseGet(() -> {
                                orgUuids.add(uuid);
                                final PartyGroup group = partyService.getGroupByUuid(uuid);
                                return Optional.ofNullable(group)
                                        .map(partyGroup -> group.getOrgGroupParentName()
                                                + Constants.STRING_SPLIT_FLAG_MIDDLE_LINE
                                                + group.getOrgGroupName())
                                        .orElse("");
                            })
                    );

                    //会议状态
                    bean.setMeetingStatus(MeetingStatusEnum.getByIntKey(bean.getMeetingStatus()));
                    //会议类型
                    final String typeRecord = Optional.ofNullable(bean.getTypeRecord())
                            .map(type -> StringUtils.split(type, TYPERECORD_DOUBLE_VERTICAL))
                            .map(Arrays::stream)
                            .orElseGet(Stream::empty)
                            .filter(StringUtils::isNotBlank)
                            .filter(Predicate.isEqual(TYPERECORD_DOUBLE_VERTICAL).negate())
                            .map(RecordTypeEnum::getByIntKey)
                            .collect(Collectors.joining(TYPERECORD_CAESURA_SIGN));

                    bean.setTypeRecord(typeRecord);
                }).collect(Collectors.toList())
        );


        // 公司下所有的党组织信息
        final String companyDn = CmpDnEnum.getValueByName(vo.getCompanyName());
        // 匹配被选中的OrgUuids
        final List<String> selectOrgUuids = vo.getOrgUuids();
        // 获取党组织结构（党委 -> 党总支（党支部）-> 党支部 -> 党小组），增加列：是否有会议记录
        sanKeReverses.addAll(partyService.getOrg(companyDn).stream()
                .filter(party -> CollectionUtils.isEmpty(selectOrgUuids) || selectOrgUuids.contains(party.getOrgUuid()))
                .peek(item -> item.setMeetingStatue(orgUuids.contains(item.getOrgUuid()) ? TRUE_STRING : FALSE_STRING))
                .collect(Collectors.toList())
        );
    }

    /**
     * 使用传入的支部uuids获取组织下的小组uuids，并且与组织数据合并
     * @param orgUuids 组织uuids
     */
    private List<String> unionPartyOrgUuidsAndPartyGroupOrgUuids(final List<String> orgUuids) {
        final List<String> partyOrgUuids = ListUtils.emptyIfNull(orgUuids);
        final List<String> partyGroupOrgUuids = partyGroupService.getPartyGroupOrgUuids(partyOrgUuids);
        return ListUtils.union(partyOrgUuids, partyGroupOrgUuids);
    }

}
